package com.wantupai.mvp.framework.support.viewstate.fragment;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;


import com.wantupai.mvp.framework.support.fragment.FragmentMvpDelegateImpl;
import com.wantupai.mvp.framework.support.manager.PresenterManager;
import com.wantupai.mvp.framework.support.viewstate.MvpViewStateDelegateCallback;
import com.wantupai.mvp.framework.support.viewstate.RestorableViewState;
import com.wantupai.mvp.framework.support.viewstate.ViewState;
import com.wantupai.mvp.mvp.MvpPresenter;
import com.wantupai.mvp.mvp.MvpView;

import java.util.UUID;


public class FragmentMvpViewStateDelegateImpl<V extends MvpView, P extends MvpPresenter<V>, VS extends ViewState<V>>
        extends FragmentMvpDelegateImpl<V, P> {
    private MvpViewStateDelegateCallback<V, P, VS> mvpViewStateDelegateCallback;
    protected Fragment fragment;
    protected static final String KEY_VIEW_ID = "cn.doolii.mvp.framework.support.viewstate.fragment.id";
    protected String viewId;
    //解决问题：横竖屏切换
    //当屏幕方向（横竖屏切换）变化的时候是否缓存Presenter实例
    protected final boolean keepPresenterInstanceDuringScreenOrientationChanges;
    //当返回的时候是否缓存presenter实例
    protected final boolean keepPresenterOnBackstack;
    //请求使用视图状态
    protected boolean applyViewState = false;
    //是否从内存中使用视图状态
    protected boolean applyViewStateFromMemory = false;
    //是否创建视图回调
    private boolean onViewCreatedCalled = false;

    //初始化参数
    public FragmentMvpViewStateDelegateImpl(Fragment fragment, MvpViewStateDelegateCallback<V, P, VS> mvpViewStateDelegateCallback
            , boolean keepPresenterInstanceDuringScreenOrientationChange, boolean keepPresenterOnBackstack) {
        super(mvpViewStateDelegateCallback);
        if (mvpViewStateDelegateCallback == null) {
            throw new NullPointerException("delegateCallback不能为空!");
        }
        if (fragment == null) {
            throw new NullPointerException("fragment不能为空");
        }
        if (!keepPresenterInstanceDuringScreenOrientationChange && keepPresenterOnBackstack) {
            //会有一个冲突问题
            //1.false:当横竖屏切换不缓存Presenter
            //2.如果返回键为true,按返回键会缓存
            throw new NullPointerException("参数异常");
        }
        this.fragment = fragment;
        this.mvpViewStateDelegateCallback = mvpViewStateDelegateCallback;
        this.keepPresenterInstanceDuringScreenOrientationChanges = keepPresenterInstanceDuringScreenOrientationChange;
        this.keepPresenterOnBackstack = keepPresenterOnBackstack;
    }

    @Override
    public void onAttach(Context context) {

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        //当activity创建的时候，需要判定之前有没有做数据缓存
        if (savedInstanceState != null && keepPresenterInstanceDuringScreenOrientationChanges) {
            //缓存-->读取数据->恢复
            viewId = savedInstanceState.getString(KEY_VIEW_ID);
            P presneter = restorePresenterOrRecreateNewPresenterAfterProcessDeath();
            mvpViewStateDelegateCallback.setPresenter(presneter);
            //读取缓存V层（数据）
            VS viewState = restoreViewStateOrRecreateViewStateAfterProcessDeath(savedInstanceState);
            mvpViewStateDelegateCallback.setViewState(viewState);
        } else {
            //重新创建
            P presenter = createViewIdAndPresenter();
            mvpViewStateDelegateCallback.setPresenter(presenter);
            VS viewState = createViewState();
            mvpViewStateDelegateCallback.setViewState(viewState);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {

    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {

    }

    @Override
    public void onStart() {
        if (!onViewCreatedCalled) {
            throw new IllegalArgumentException("必需要进行回调...");
        }
        //准备数据
        //MVP回调是有顺序的
        if (applyViewState) {
            //是否现实这个视图
            VS viewState = mvpViewStateDelegateCallback.getViewState();
            V mvpView = mvpViewStateDelegateCallback.getMvpView();
            if (viewState == null) {
                throw new NullPointerException("ViewState not null");
            }
            //恢复
            mvpViewStateDelegateCallback.setRestoringViewState(true);
            viewState.apply(mvpView, applyViewStateFromMemory);
            mvpViewStateDelegateCallback.setRestoringViewState(false);
        }

        //绑定V层
        mvpViewStateDelegateCallback.getPresenter().attach(mvpViewStateDelegateCallback.getMvpView());
        if (applyViewState) {
            if (!applyViewStateFromMemory && keepPresenterInstanceDuringScreenOrientationChanges) {
                //applyViewStateFromMemory
                //false:表示缓存中不存在数据
                //keepPresenterInstanceDuringScreenOrientationChanges
                //true:表示横竖屏切换需要缓存
                //说白了：内存中不存在缓存数据，这个时候我们需要缓存新的数据
                if (viewId == null) {
                    throw new IllegalArgumentException("ViewId not null...");
                }
                PresenterManager.putViewState(getActivity(), viewId, mvpViewStateDelegateCallback.getViewState());
            }
            mvpViewStateDelegateCallback.onViewStateInstanceRestored(applyViewStateFromMemory);
        } else {
            mvpViewStateDelegateCallback.onNewViewStateInstance();
        }
    }

    @Override
    public void onPause() {

    }

    @Override
    public void onResume() {

    }

    @Override
    public void onStop() {
        mvpViewStateDelegateCallback.getPresenter().detach();
        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            applyViewState = true;
            applyViewStateFromMemory = true;
        } else {
            applyViewState = false;
            applyViewStateFromMemory = false;
        }
    }

    @Override
    public void onDestroyView() {
        onViewCreatedCalled = false;
    }

    @Override
    public void onDestroy() {
        Activity activity = getActivity();
        boolean retainPresenterInstance = FragmentMvpDelegateImpl.retainPresenterInstance(activity, fragment, keepPresenterInstanceDuringScreenOrientationChanges, keepPresenterOnBackstack);
        P presenter = mvpViewStateDelegateCallback.getPresenter();
        //是否保留数据
        if (!retainPresenterInstance) {
            //false：表示需要销毁，不在使用了
            presenter.destory();
        }

        //清空缓存
        if (!retainPresenterInstance && viewId != null) {
            PresenterManager.remove(activity, viewId);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if ((keepPresenterInstanceDuringScreenOrientationChanges || keepPresenterOnBackstack) && outState != null) {
            //缓存
            outState.putString(KEY_VIEW_ID, viewId);
        }

        boolean retainPresenterInstance = FragmentMvpDelegateImpl.retainPresenterInstance(getActivity(), fragment, keepPresenterInstanceDuringScreenOrientationChanges, keepPresenterOnBackstack);
        VS viewState = mvpViewStateDelegateCallback.getViewState();
        if (viewState == null) {
            throw new NullPointerException("cn.doolii.mvp.framework.base.view state not null...");
        }

        if (retainPresenterInstance && viewState instanceof RestorableViewState) {
            //保存到->序列号保存
            //通过数据...
            ((RestorableViewState) viewState).onSaveInstanceState(outState);
        }
    }

    @Override
    public void onDetach() {

    }

    private Activity getActivity() {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            throw new NullPointerException(
                    "Activity returned by Fragment.getActivity() is null. Fragment is " + fragment);
        }
        return activity;
    }

    //恢复Presenter或者在进程死亡之后重新创建Presenter
    private P restorePresenterOrRecreateNewPresenterAfterProcessDeath() {
        P presenter;
        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            if (viewId != null && (presenter = PresenterManager.getPresenter(getActivity(), viewId)) != null) {
                return presenter;
            }
        }
        presenter = createViewIdAndPresenter();
        return presenter;
    }

    private P createViewIdAndPresenter() {
        P presenter = mvpViewStateDelegateCallback.createPresenter();
        if (presenter == null) {
            throw new NullPointerException(
                    "Presenter returned from createPresenter() is null. Fragment is " + fragment);
        }

        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            viewId = UUID.randomUUID().toString();
            PresenterManager.putPresenter(getActivity(), viewId, presenter);
        }
        return presenter;
    }

    private VS restoreViewStateOrRecreateViewStateAfterProcessDeath(Bundle bundle) {

        if (bundle == null) {
            throw new NullPointerException("Bundle is null. This should never be the case " +
                    "Please report this issue at https://github.com/sockeqwe/mosby/issues");
        }

        if (viewId == null) {
            throw new NullPointerException(
                    "The (internal) Mosby View id is null although bundle is not null. "
                            + "This should never be the case while restoring ViewState instance. "
                            + "Please report this issue at https://github.com/sockeqwe/mosby/issues");
        }

        VS viewState = PresenterManager.getViewState(fragment.getActivity(), viewId);
        if (viewState != null) {
            applyViewState = true;
            applyViewStateFromMemory = true;
            return viewState;
        }

        viewState = mvpViewStateDelegateCallback.createViewState();
        if (viewState == null) {
            throw new NullPointerException(
                    "ViewState returned from createViewState() is null! MvpView that has returned null as ViewState is: "
                            + mvpViewStateDelegateCallback.getMvpView());
        }

        if (viewState instanceof RestorableViewState) {
            // A little bit hacky that we need an instance of the viewstate to restore a cn.doolii.mvp.framework.base.view state
            // (may creates another cn.doolii.mvp.framework.base.view state object) but I don't know any better way :)
            RestorableViewState restoredViewState =
                    ((RestorableViewState) viewState).onRestoreInstanceState(bundle);

            if (restoredViewState != null) {
                //
                // ViewState restored from bundle
                //
                viewState = (VS) restoredViewState;
                applyViewState = true;
                applyViewStateFromMemory = false;

                if (keepPresenterInstanceDuringScreenOrientationChanges) {
                    PresenterManager.putViewState(getActivity(), viewId, viewState);
                }
                return viewState;
            }
        }

        //
        // Entirely new ViewState has been created, typically because process death and mosby cn.doolii.mvp.framework.base.view id points to
        // a  old id but cn.doolii.mvp.framework.base.view got a new one because of process death.
        //

        applyViewState = false;
        applyViewStateFromMemory = false;

        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            PresenterManager.putViewState(getActivity(), viewId, viewState);
        }
        return viewState;
    }

    private VS createViewState() {
        VS viewState = mvpViewStateDelegateCallback.createViewState();
        if (viewState == null) {
            throw new NullPointerException(
                    "ViewState returned from createViewState() is null. Fragment is " + fragment);
        }

        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            PresenterManager.putViewState(getActivity(), viewId, viewState);
        }

        return viewState;
    }
}
